#ifndef QUERY_QUERY_H
#define QUERY_QUERY_H 1

#include <stdarg.h>

#include <common/macro/build.h>
#include <transaction/transaction.h>
#include <query/result.h>
#include <storage/storage.h>
#include <storage/itempointer.h>
#include <storage/page.h>
#include <storage/tuple/tuple.h>

typedef struct query_t query_t;

typedef struct condition_t condition_t;

typedef enum query_phase_e {
	QUERY_PHASE_UNPREPARED,
	QUERY_PHASE_PREPARED,
	QUERY_PHASE_EXECUTED,
	QUERY_PHASE_STEPPING,
} query_phase_e;

typedef enum query_type_e {
	QUERY_SELECT,
	QUERY_INSERT,
	QUERY_DELETE,
	QUERY_UPDATE,
} query_type_e;

typedef enum result_e (*query_iterator_openpage_f)(query_t *, page_number_t, page_t **);
typedef enum result_e (*query_iterator_closepage_f)(query_t *, page_number_t, page_t *);

typedef int (*query_callback_f)(query_t *, void *ctx);

typedef enum query_iterator_status_e {
	QUERY_ITERATOR_OPEN,
	QUERY_ITERATOR_CLOSED,
} query_iterator_status_e;

typedef struct query_iterator_t {
	item_pointer_t pointer;
	page_t *page;
	tuple_t *tuple;

	query_iterator_status_e status;
	query_iterator_openpage_f openpage;
	query_iterator_closepage_f closepage;
} query_iterator_t;

typedef struct delete_blocker_t {
	page_item_t blocker_item;
	transaction_id_t blocker_id;
} delete_blocker_t;

typedef struct delete_ctx_t {
	/* Did we actually ever modify the page? */
	int dirty;

	/* List of items in the page that are possibly blocking. */
	delete_blocker_t *blockers;
	page_item_t blockers_c;
} delete_ctx_t;

typedef struct insert_item_t {
	char **data;
	size_t *sizes;
	tuple_t tuple;
} insert_item_t;

typedef struct insert_ctx_t {
	/* Is the data set for the upcomming row? */	
	int data_set;

	insert_item_t current;
	insert_item_t *queue;
	int queue_c;
} insert_ctx_t;

typedef struct update_ctx_t {
	int need_rescan;

	transaction_id_t *future_deletes;
	int future_deletes_c;

	/* Callback, used for UPDATE. */
	query_callback_f funcptr;
	void *ctx;
} update_ctx_t;

typedef struct condition_list_t {
	condition_t **conditions;
	int conditions_c;
} condition_list_t;

struct query_t {
	/* Query status and type */
	query_type_e type;
	query_phase_e phase;

	/* Transaction to which this query is bound. */
	transaction_t *transaction;

	/* Table and storage upon which this query operates. */
	table_t *table;
	storage_t *storage;

	/* Data iterator. */
	query_iterator_t iterator;

	/* Data retrieval cache for BLOBs. */
	char **blob_cache_data;
	size_t *blob_cache_sizes;

	/* Conditions used for matching. */
	condition_list_t **condition_lists;
	int condition_lists_c;
	void **condition_values;

	/* Command context */
	delete_ctx_t delete_ctx;
	insert_ctx_t insert_ctx;
	update_ctx_t update_ctx;
};

API_PUBLIC query_t *query_new(transaction_t *);
API_PUBLIC void query_destroy(query_t *);
API_PUBLIC void query_append_conditions_list(query_t *, int, condition_t **);
API_PUBLIC void query_append_conditions(query_t *, ...);
API_PUBLIC void query_data_set_list(query_t *, char **, size_t *);
API_PUBLIC void query_data_set(query_t *,...);
API_PUBLIC result_e query_column_get_id(query_t *, char *, int *);
API_PUBLIC result_e query_data_get(query_t *, int, char **, size_t *);
API_PUBLIC result_e query_datatype_get(query_t *, int, char **);
API_PUBLIC result_e query_datatype_args_get(query_t *, int, char ***, int *);
API_PUBLIC void query_update_callback(query_t *, query_callback_f, void *);
API_PUBLIC result_e query_prepare(query_t *, query_type_e, const char *);
API_PUBLIC result_e query_step(query_t *);
API_PUBLIC result_e query_execute(query_t *);
API_PUBLIC result_e query_finalize(query_t *);


#endif
